package com.tbg.bitpaypos.app; import android.annotation.TargetApi; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattServerCallback; import android.bluetooth.BluetoothGattService; import android.bluetooth.le.AdvertiseCallback; import android.bluetooth.le.AdvertiseSettings; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.BitmapFactory; import android.graphics.Point; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorManager; import android.hardware.SensorEventListener; import android.media.AudioManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.NfcEvent; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Parcelable; import android.os.SystemClock; import android.support.v7.app.ActionBarActivity; import android.util.Base64; import android.util.Log; import android.view.Display; import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.gocoin.api.GoCoin; import com.gocoin.api.JSON; import com.gocoin.api.pojo.Token; import com.gocoin.api.services.InvoiceService; import com.m1pay.nfsound.NFSoundActivity; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpRequestBase; import org.json.JSONArray; import org.json.JSONException; import org.json.simple.JSONObject; import org.json.simple.JSONValue; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.UUID; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import ch.boye.httpclientandroidlib.impl.client.HttpClientBuilder; @TargetApi(Build.VERSION_CODES.L) public class DisplayMessageActivity extends ActionBarActivity implements SensorEventListener, NfcAdapter.CreateNdefMessageCallback { // accelerometer info private float mLastX, mLastY, mLastZ; private boolean mInitialized; private SensorManager mSensorManager; private Sensor mAccelerometer; private final float NOISE = (float) 2.0; private volatile String invoiceURL = ""; private volatile double price = 0.0; private volatile String btcPrice; private volatile String btcAddress; // coinbase button id private volatile String coinBaseID; // this is order id assigned by coinbase // merchant id on receipt goes on custom id in coinbase private volatile String coinbaseOrderID = ""; // initization of order id params which takes order id message // declarations for bitpay private volatile InvoiceParams orderIDParam = null; private volatile Invoice invoice = null; private volatile BitPay bitpay = null; // gocoin declarations private volatile com.gocoin.api.pojo.Invoice goCoinInvoiceSetup = new com.gocoin.api.pojo.Invoice(); private volatile InvoiceService goCoinInvoiceService; private volatile Token goCoinToken; private String serviceOneCharUuid; // used to check whether payment is confirmed or not private volatile boolean notConfirmed = true; private volatile NfcAdapter mNfcAdapter; private volatile TextView textView; // which pos we are using private int whichPOS = 1; private volatile CoinBase coinBase; private volatile JSONObject result; private volatile boolean foreground = true; // locks used so can wait until invoice is created // before checking for confirmation / transmitting nfc message private volatile Lock lock = new ReentrantLock(); private volatile Lock nfcLock = new ReentrantLock(); private volatile Lock refundLock = new ReentrantLock(); private DisplayMessageActivity thisClass = this; // used for if exception thrown when checking transaction confirmation private Thread.UncaughtExceptionHandler h; private volatile boolean apiIsAGo = true; // used for storing bitcoin uri private volatile String url = ""; private NFSoundActivity sound = null; private volatile boolean soundPlaying = false; // vars for wallet balance private volatile long balance; private volatile long unconfirmed_balance; private double initialWalletBalanceBtc; String bitpayApiKey; String coinbaseApiKey; String gocoinAPIKey; String gocoinMerchantID; String coinbaseSecret; String publicAddress; // used for ble wallet communications BluetoothUtility ble; private static final String SERVICE_UUID_1 = "00001802-0000-1000-8000-00805f9b34fb"; /** * Get price and api keys from main screen * Decide which PoS we're using and call its methods */ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_display_message); android.content.Intent intent = getIntent(); // this Gets the message order id and price from business String idMessage = intent.getStringExtra(MainActivity.EXTRA_MESSAGE); String priceMessage = intent.getStringExtra(MainActivity.EXTRA_MESSAGE2); price = Double.parseDouble(priceMessage); // this gets whichever POS we're using // defaults to 1 (BitPay) // check for null api keys and redirect to settings if it's so whichPOS = intent.getIntExtra("POS_TYPE", 1); bitpayApiKey = intent.getStringExtra("bitpayAPIKey"); coinbaseApiKey = intent.getStringExtra("coinbaseAPIKey"); gocoinAPIKey = intent.getStringExtra("gocoinAPIKey"); gocoinMerchantID = intent.getStringExtra("goCoinMerchantID"); coinbaseSecret = intent.getStringExtra("coinbaseSecret"); publicAddress = intent.getStringExtra("publicAddress"); new Thread() { @Override public void run() { // m accelerometer stuff // used to detect movement and play sound mInitialized = false; mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mSensorManager.registerListener(thisClass, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); } }.run(); // if whichPOS == 1 then bitpay else if 2 call coinbase else if 3 call gocoin methods // if 4 then call personal address methods if (whichPOS == 1 && isNetworkAvailable()) { bitPayPOS(idMessage, bitpayApiKey); } else if (whichPOS == 2 && isNetworkAvailable()) { coinBasePOS(idMessage, coinbaseApiKey, coinbaseSecret, priceMessage); } else if (whichPOS == 3 && isNetworkAvailable()) { goCoinPOS(idMessage, gocoinAPIKey, priceMessage, gocoinMerchantID); } else if (whichPOS == 4 && isNetworkAvailable()) { btcAddress = publicAddress; new Thread() { @Override public void run() { checkTxChain(); initialWalletBalanceBtc = ((double)(unconfirmed_balance)/10000000); } }.start(); Log.d("WHAT", "!"+initialWalletBalanceBtc); ownWallet(idMessage, priceMessage, publicAddress); } else { Log.d("Error", "Network is not available"); Context context = getApplicationContext(); CharSequence text = "Network is not available, enable a wifi connection"; Toast toast = Toast.makeText(context, text, 2); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); // go to main screen android.content.Intent intentMain = new Intent(thisClass, MainActivity.class); startActivity(intentMain); finish(); } } public void onAccuracyChanged(Sensor sensor, int accuracy) { // not needed } /** check if BLE Supported device */ public static boolean isBLESupported(Context context) { return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE); } /** * Used to play near field sound when sensor tapped * @param event - accelerometer event */ public void onSensorChanged(SensorEvent event){ // In this example, alpha is calculated as t / (t + dT), // where t is the low-pass filter's time-constant and // dT is the event delivery rate. String X = ""; String Y = ""; String Z = ""; // 0 is x, 1 is y, 2 is z float x = event.values[0]; float y = event.values[1]; float z = event.values[2]; if(!mInitialized) { mLastX = x; mLastY = y; mLastZ = z; X = "0.0"; Y = "0.0"; Z = "0.0"; mInitialized = true; } else { float deltaX = Math.abs(mLastX - x); float deltaY = Math.abs(mLastY - y); float deltaZ = Math.abs(mLastZ - z); if (deltaX < NOISE) deltaX = (float)0.0; if (deltaY < NOISE) deltaY = (float)0.0; if (deltaZ < NOISE) deltaZ = (float)0.0; mLastX = x; mLastY = y; mLastZ = z; X = Float.toString(deltaX); Y = Float.toString(deltaY); Z = Float.toString(deltaZ); // Log.d("DIFF", X+" "+Y+" "+Z); if (deltaZ > 1.5) { try { if (sound != null && !url.equals("") && foreground && apiIsAGo && notConfirmed && !soundPlaying) { soundPlaying = true; sound.sendString(); Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { if(sound!=null) { sound.sendString(); } Handler handlerBool = new Handler(); handlerBool.postDelayed(new Runnable() { @Override public void run() { soundPlaying = false; } }, 1400); // 5000ms delay } }, 1400); // 5000ms delay } } catch (Exception e) { Log.d("Error", e.toString()); } } } } /** * Creates a bitpay instance + invoice * @param idMessage - order id * @param bitpayApiKey */ public void bitPayPOS(final String idMessage, final String bitpayApiKey) { // Creates an invoice using params passed from mainactivity orderIDParam = new InvoiceParams(); orderIDParam.setOrderId(idMessage); // create the exception handler to catch uncaught exception from bitpay class // e.g. null apikey Thread.UncaughtExceptionHandler handle = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { System.out.println("Uncaught exception: " + ex); ex.printStackTrace(); Context context = getApplicationContext(); CharSequence text = "Network is not available, enable a wifi connection"; Toast toast = Toast.makeText(context, text, 2); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); android.content.Intent intent = new Intent(thisClass, MainActivity.class); startActivity(intent); } }; // new thread needed for network activities Thread t = new Thread() { @Override public void run() { // have to use lock b/c need to check for invoice in while loop later lock.lock(); refundLock.lock(); nfcLock.lock(); try { // check for null api keys and redirect to settings if it's so!!! // else create bitpay invoice and get its url try { bitpay = new BitPay(bitpayApiKey, "USD"); invoice = bitpay.createInvoice(price, orderIDParam); invoiceURL = invoice.getUrl(); url = bitpay.getBitcoinUrl(invoiceURL); // Log.d("URLBitpay", url); // fetch address for QR Creation text String bitcoinURI = url.substring(url.indexOf("b"), url.indexOf("&")+1); int start = url.indexOf("<a href=\"bitcoin"); int end = url.indexOf("?"); btcPrice = url.substring((url.indexOf("?")+8), url.indexOf("&")); btcAddress = url.substring((start+9),end); } // if api key is wrong we get a null pointer // go to settings screen in this case catch (NullPointerException e) { apiIsAGo = false; android.content.Intent intentSettings = new Intent(thisClass, SettingsActivity.class); intentSettings.putExtra("apifail?", true); startActivity(intentSettings); finish(); // Log.d("AA", "" + apiIsAGo); } } finally { // other threads can now access this info lock.unlock(); nfcLock.unlock(); refundLock.unlock(); } // provided API key is correct + network is working // we create the qr code DisplayMessageActivity.this.runOnUiThread(new Runnable() { @Override public void run() { if (apiIsAGo) { doQRCreation(btcAddress, btcPrice); } } }); } }; t.setUncaughtExceptionHandler(handle); t.start(); // check to see if invoice is paid // checking done on new thread to save rsc + can't be done on ui thread checkBitpayInvoice(); } /** * Creates a transaction invoice when using a personal / business wallet service * @param idMessage * @param priceString * @param publicAddress */ public void ownWallet(final String idMessage, final String priceString, final String publicAddress) { // create the exception handler to catch uncaught exception // e.g. null result from api call Thread.UncaughtExceptionHandler handle = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { System.out.println("Uncaught exception: " + ex); ex.printStackTrace(); Context context = getApplicationContext(); CharSequence text = "Network is not available, enable a wifi connection"; Toast toast = Toast.makeText(context, text, 2); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); android.content.Intent intent = new Intent(thisClass, MainActivity.class); startActivity(intent); } }; // new thread needed for network activities Thread t = new Thread() { @Override public void run() { // have to use lock b/c need to check for invoice in while loop later lock.lock(); nfcLock.lock(); try { // check for null api keys and redirect to settings if it's so!!! // else create bitpay invoice and get its url try { // need to get price w/ coindesk btcAddress = publicAddress; String priceUSD = fetchBitcoinPrice(); double priceBtc = (Double.parseDouble(priceString)/Double.parseDouble(priceUSD)); btcPrice = String.format("%.8f", priceBtc); url = "bitcoin:"+publicAddress+"?amount="+btcPrice; // Log.d("URLBitpay", url); } // go to settings screen in case of failed api call to fetch price catch (NullPointerException e) { apiIsAGo = false; android.content.Intent intentSettings = new Intent(thisClass, SettingsActivity.class); intentSettings.putExtra("apifail?", true); startActivity(intentSettings); finish(); // Log.d("AA", "" + apiIsAGo); } } finally { // other threads can now access this info lock.unlock(); nfcLock.unlock(); } // provided API key is correct + network is working // we create the qr code DisplayMessageActivity.this.runOnUiThread(new Runnable() { @Override public void run() { if (apiIsAGo) { doQRCreation(btcAddress, btcPrice); } } }); } }; t.setUncaughtExceptionHandler(handle); t.start(); // check to see if invoice is paid // checking done on new thread to save rsc + can't be done on ui thread checkWalletPaid(); } /** * Fetches current bitcoin/usd price to calculate prices for people not using a btc payment * processing service - this uses Chain Api * * @return String of the price */ public String fetchBitcoinPrice() { ch.boye.httpclientandroidlib.client.methods.HttpRequestBase request; request = new ch.boye.httpclientandroidlib.client.methods.HttpGet("https://api.coindesk.com/v1/bpi/currentprice/USD.json"); // request.setHeader("Content-type", "application/json"); ch.boye.httpclientandroidlib.client.HttpClient httpClient = HttpClientBuilder.create().build(); ch.boye.httpclientandroidlib.HttpResponse response = null; try { response = httpClient.execute(request); } catch (IOException e) { e.printStackTrace(); } ch.boye.httpclientandroidlib.HttpEntity entity = response.getEntity(); if (entity != null) { // get the response and make it a buffered reader from the input stream from the response entity BufferedReader rd = null; try { rd = new BufferedReader(new InputStreamReader(entity.getContent())); } catch (IOException e) { e.printStackTrace(); } // make a stringbuilder StringBuilder content = new StringBuilder(); String line; // read from the buffered reader and feed it to the stringbuilder try { while (null != (line = rd.readLine())) { content.append(line); } } catch (IOException e) { e.printStackTrace(); } // bitpay forgot to close reader try { rd.close(); } catch (IOException e) { e.printStackTrace(); } // make the content a string then parse it and create jsonfinal result Object obj = JSONValue.parse(content.toString()); JSONObject finalResult = (JSONObject) obj; Log.d("Result", finalResult.toJSONString()); JSONObject bpi = (JSONObject) finalResult.get("bpi"); JSONObject usd = (JSONObject) bpi.get("USD"); String usdPrice = usd.get("rate_float").toString(); return usdPrice; } else { return null; } } /** * Uses chain api to check if a payment was made to the bitcoin address * records the address's unconfirmed balance */ public void checkTxChain() { ch.boye.httpclientandroidlib.client.methods.HttpRequestBase request; request = new ch.boye.httpclientandroidlib.client.methods.HttpGet("https://api.chain.com/v1/bitcoin/addresses/"+btcAddress); String encoding = new String(Base64.encode(("38602a33683645763a2165f8223807f6:c990967a2e822424726467d40aa400c5".getBytes()), Base64.NO_WRAP)); request.addHeader("Authorization", "Basic " + encoding); // request.setHeader("Content-type", "application/json"); ch.boye.httpclientandroidlib.client.HttpClient httpClient = HttpClientBuilder.create().build(); ch.boye.httpclientandroidlib.HttpResponse response = null; try { response = httpClient.execute(request); } catch (Exception e) { e.printStackTrace(); } ch.boye.httpclientandroidlib.HttpEntity entity = response.getEntity(); if (entity != null) { // get the response and make it a buffered reader from the input stream from the response entity BufferedReader rd = null; try { rd = new BufferedReader(new InputStreamReader(entity.getContent())); } catch (IOException e) { e.printStackTrace(); } // make a stringbuilder StringBuilder content = new StringBuilder(); String line; // read from the buffered reader and feed it to the stringbuilder try { while (null != (line = rd.readLine())) { content.append(line); } } catch (IOException e) { e.printStackTrace(); } // bitpay forgot to close reader try { rd.close(); } catch (IOException e) { e.printStackTrace(); } // make the content a string then parse it and create jsonfinal result Object obj = JSONValue.parse(content.toString()); JSONObject finalResult = (JSONObject) obj; // Log.d("Result", finalResult.toJSONString()); balance = (Long) finalResult.get("balance"); unconfirmed_balance = (Long) finalResult.get("unconfirmed_received"); } } /** * Creates a coinbase instance + invoice * @param idMessage - order id * @param idMessage - order id * @param coinbaseApiKey * @param coinbaseSecret * @param priceMessage */ public void coinBasePOS(final String idMessage, final String coinbaseApiKey, final String coinbaseSecret, final String priceMessage) { // create the exception handler to catch uncaught exception from coinbase class // e.g. null apikey Thread.UncaughtExceptionHandler handle = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { System.out.println("Uncaught exception: " + ex); ex.printStackTrace(); Context context = getApplicationContext(); CharSequence text = "Network is not available, enable a wifi connection"; Toast toast = Toast.makeText(context, text, 2); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); android.content.Intent intent = new Intent(thisClass, MainActivity.class); startActivity(intent); } }; // new thread needed for network activities Thread t = new Thread() { @Override public void run() { // have to use lock b/c need to check for invoice in while loop later lock.lock(); refundLock.lock(); nfcLock.lock(); try { // check for null api keys and redirect to settings if it's so!!! // else create coinbase invoice and get its url try { coinBase = new CoinBase(coinbaseApiKey, coinbaseSecret); //Log.d("W", "coinbase created"); //Log.d("W", coinbaseApiKey); //Log.d("W", coinbaseSecret); JSONObject invoiceCoinbase = new JSONObject(); invoiceCoinbase.put("price_currency_iso", "USD"); invoiceCoinbase.put("price_string", priceMessage); invoiceCoinbase.put("type", "buy_now"); invoiceCoinbase.put("name", idMessage); invoiceCoinbase.put("custom", idMessage); JSONObject json = new JSONObject(); json.put("button", invoiceCoinbase); // Log.d("INVOICE", json.toString()); try { result = coinBase.postHttp("https://coinbase.com/api/v1/orders", json.toString()); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // JSONObject s = (JSONObject) result.get("order"); // s.get("status"); // Log.d("WHAT", (String) s.get("status")); // Log.d("RESULT", result.toString()); JSONObject order = (JSONObject) result.get("order"); JSONObject button = (JSONObject) order.get("button"); // Log.d("BUTTON", button.toString()); coinBaseID = (String) button.get("id"); // Log.d("COINBASEID", coinBaseID); // Log.d("ORDER", order.toString()); JSONObject totalBtc = (JSONObject) order.get("total_btc"); String btcPricePrelim = totalBtc.get("cents").toString(); double price = (Double.parseDouble(btcPricePrelim)/100000000); btcPrice = String.format("%.8f", price); // Log.d("PRICE", btcPrice); btcAddress = (String) order.get("receive_address"); url = ("bitcoin:"+btcAddress+"?amount="+btcPrice+"&r=https%3A%2F%2Fcoinbase.com%2Fcheckouts%2F"+coinBaseID); coinbaseOrderID = (String) order.get("id"); Log.d("WWWW", coinbaseOrderID+btcPrice); } // if api key is wrong we get a null pointer // go to settings screen in this case catch (NullPointerException e) { apiIsAGo = false; android.content.Intent intentSettings = new Intent(thisClass, SettingsActivity.class); intentSettings.putExtra("apifail?", true); startActivity(intentSettings); finish(); // Log.d("AA", "" + apiIsAGo+e); e.printStackTrace(); } } finally { // other threads can now access this info lock.unlock(); nfcLock.unlock(); refundLock.unlock(); } // provided API key is correct + network is working // we create the qr code DisplayMessageActivity.this.runOnUiThread(new Runnable() { @Override public void run() { if (apiIsAGo) { doQRCreation(btcAddress, btcPrice); // check to see if invoice is paid } } }); } }; t.setUncaughtExceptionHandler(handle); t.start(); // checking done on new thread to save rsc + can't be done on ui thread if (coinbaseOrderID != null) { checkCoinbaseInvoice(); } else { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { checkCoinbaseInvoice(); } }, 7500); } } /** * Creates a goCoin instance + invoice * @param idMessage - order id * @param goCoinAPIKey * @param priceMessage - price * @param gocoinMerchantID */ public void goCoinPOS(final String idMessage, final String goCoinAPIKey, final String priceMessage, final String gocoinMerchantID) { // create the exception handler to catch uncaught exception from gocoin class // e.g. null apikey Thread.UncaughtExceptionHandler handle = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { System.out.println("Uncaught exception: " + ex); ex.printStackTrace(); Context context = getApplicationContext(); CharSequence text = "Network is not available, enable a wifi connection"; Toast toast = Toast.makeText(context, text, 2); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); android.content.Intent intent = new Intent(thisClass, MainActivity.class); startActivity(intent); } }; // new thread needed for network activities Thread t = new Thread() { @Override public void run() { // have to use lock b/c need to check for invoice in while loop later lock.lock(); nfcLock.lock(); // check for null api keys and redirect to settings if it's so!!! // else create gocoin invoice and get its url try { goCoinInvoiceService = GoCoin.getInvoiceService(); // invoice setup is the invoice we're creating which we pass into the service to create // a real invoice // this is what currency we input the price in goCoinInvoiceSetup.setBasePriceCurrency("USD"); // this is the currency we get paid in (btc) goCoinInvoiceSetup.setPriceCurrency("BTC"); // set the usd price to whatever we input goCoinInvoiceSetup.setBasePrice(priceMessage); // set order id goCoinInvoiceSetup.setOrderId(idMessage); goCoinToken = new Token(goCoinAPIKey,"",""); // create an invoice using token, merchantid, and our invoice params we setup above goCoinInvoiceSetup = goCoinInvoiceService.createInvoice(goCoinToken, gocoinMerchantID, goCoinInvoiceSetup); // retrieve the invoice and get it's url + btc price info below goCoinInvoiceSetup = goCoinInvoiceService.getInvoice(goCoinToken, goCoinInvoiceSetup.getId()); btcPrice = goCoinInvoiceSetup.getPrice(); // makes a bitcoin url/uri url = ("bitcoin:"+goCoinInvoiceSetup.getPaymentAddress()+"?amount="+goCoinInvoiceSetup.getPrice()+"&r=https%3A%2F%2Fgateway.gocoin.com%2Finvoices%2F"+goCoinInvoiceSetup.getId()); } // if api key is wrong we get a null pointer // go to settings screen in this case catch (NullPointerException e) { apiIsAGo = false; android.content.Intent intentSettings = new Intent(thisClass, SettingsActivity.class); intentSettings.putExtra("apifail?", true); startActivity(intentSettings); finish(); Log.d("AA", "" + apiIsAGo); } finally { // other threads can now access this info lock.unlock(); nfcLock.unlock(); } // provided API key is correct + network is working // we create the qr code and do sound DisplayMessageActivity.this.runOnUiThread(new Runnable() { @Override public void run() { if (apiIsAGo) { btcAddress = goCoinInvoiceSetup.getPaymentAddress(); doQRCreation(btcAddress, btcPrice); } } }); } }; t.setUncaughtExceptionHandler(handle); t.start(); // check to see if invoice is paid // checking done on new thread to save rsc + can't be done on ui thread checkGoCoinInvoice(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.display_message, menu); Log.d("K", "options created"); return true; } /** * Start NFC behavior */ public void startNFC() { nfcLock.lock(); try { PackageManager pm = getPackageManager(); if (pm.hasSystemFeature(PackageManager.FEATURE_NFC) && !url.equals("") && Build.VERSION.SDK_INT>=16 && foreground) { mNfcAdapter = NfcAdapter.getDefaultAdapter(this); if (mNfcAdapter != null && mNfcAdapter.isEnabled()) { mNfcAdapter.setNdefPushMessageCallback(this, this); // mNfcAdapter.enableForegroundNdefPush(thisClass, this.createNdefMessage()); } } } catch (Exception e) { e.printStackTrace(); Log.d("Error", "Error doing NFC"); Context context = getApplicationContext(); CharSequence text = "Enable NFC + Android beam (not S beam)"; Toast toast = Toast.makeText(context, text, 2); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); } finally { nfcLock.unlock(); } } /** * Creates nfc message from uri * * @return msg - nfc message (bitcoin uri) */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override public NdefMessage createNdefMessage(NfcEvent event) { if(Build.VERSION.SDK_INT >=16 && !url.equals("") && foreground) { NdefRecord nfcUriRecord = NdefRecord.createUri(url); NdefMessage msg = new NdefMessage(nfcUriRecord); tapScreen(); return msg; } else { return null; } } /** * Tap screen automatically to bypass "tap for nfc screen" */ public void tapScreen() { DisplayMessageActivity.this.runOnUiThread(new Runnable() { @Override public void run() { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { try { Process p = Runtime.getRuntime().exec("su"); DataOutputStream outs = new DataOutputStream(p.getOutputStream()); // get screen center programmatically Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.x; int height = size.y; int widthHalf = width/2; int heightHalf = height/2; // String cmd="input tap 368 669"; String cmd = "input tap "+widthHalf+" "+heightHalf; outs.writeBytes(cmd+"\n"); } catch (IOException e) { e.printStackTrace(); } } }, 250); } }); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); // Log.d("K", "options selected"); if (id == R.id.action_settings) { android.content.Intent intent = new Intent(this, SettingsActivity.class); startActivity(intent); return true; } if(id==R.id.home) { this.onBackPressed(); } return super.onOptionsItemSelected(item); } /** * Creates the qr from bitcoin URI * Also begins NFC after QR created (also guarantees activity is in foreground) * Doing NFC this way makes sure invoice is created before setting message * @param btcAddress */ private void doQRCreation(String btcAddress, String btcPricing) { // check if twice incase in between we change orientation try { if (foreground && isNetworkAvailable()) { // display order info on the screen TextView orderInfo = (TextView) findViewById(R.id.order_info); orderInfo.setText("$" + Double.toString(price) + "\n" + "\n" + "Status: Unpaid" + "\n \n" + "Address: " + btcAddress); // create a qr code from the url File file = QRCode.from(url).file(); ImageView image = (ImageView) findViewById(R.id.qrImg); android.graphics. Bitmap myQRImg = BitmapFactory.decodeFile(file.getAbsolutePath()); myQRImg = myQRImg.createScaledBitmap(myQRImg, 500, 500, true); image.setImageBitmap(myQRImg); } } catch (Exception e) { Log.d("Error", "Exception while creating QR"+e); e.printStackTrace(); } // provided we're in the foreground start sound (a few initial plays - the rest occur via accelerometer) & nfc if(foreground) { startNFC(); String soundUrl = "b:"+btcAddress+"?"+btcPricing+"&"; Log.d("Soundurl", soundUrl); // start sound too if(!url.equals("") && foreground && apiIsAGo && notConfirmed && !soundPlaying) { soundPlaying = true; sound = new NFSoundActivity(soundUrl, (AudioManager) this.getSystemService(Context.AUDIO_SERVICE)); Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { if(sound!=null) { sound.sendString(); } Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { if(sound!=null) { sound.sendString(); } Handler handlerBool = new Handler(); handlerBool.postDelayed(new Runnable() { @Override public void run() { soundPlaying = false; } }, 1400); // 5000ms delay } }, 3750); // 5000ms delay } }, 1400); // 5000ms delay } } } /** * Goes to confirmation screen */ private void confirmed() { android.content.Intent intent = new Intent(this, DisplayConfirmationActivity.class); intent.putExtra("COINBASE_ORDER_ID", coinbaseOrderID); startActivity(intent); } /** * returns payment invoice from bitpay * @return invoice */ private Invoice getInvoice() { if (whichPOS == 1) { return bitpay.getInvoice(invoice.getId()); } else { return null; } } /** * Check if the bitpay invoice is paid */ public void checkBitpayInvoice() { // catch any uncaught exceptions thrown by getting invoice status // should be caught by bitpay class h = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { System.out.println("Uncaught exception: " + ex); Context context = getApplicationContext(); CharSequence text = "Network is not available, enable a wifi connection"; Toast toast = Toast.makeText(context, text, 2); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); android.content.Intent intent = new Intent(thisClass, MainActivity.class); startActivity(intent); finish(); } }; Thread c = new Thread() { @Override public void run() { // lock thread so it only accesses invoice after it's created lock.lock(); try { if(apiIsAGo) { while (notConfirmed) { Thread.sleep(800); // get invoice and check its payment status String status = getInvoice().getStatus(); if (status.equals("paid") || status.equals("confirmed")) { notConfirmed = false; // go to confirmed screen confirmed(); } } } } catch (InterruptedException e) { // do nothing // pause is intendec } finally { lock.unlock(); } } }; c.setUncaughtExceptionHandler(h); c.start(); } /** * Checking if coinbase invoice is paid */ public void checkCoinbaseInvoice() { // catch any uncaught exceptions thrown by getting invoice status // should be caught by coinbase class // this id is one assigned by coinbase // Log.d("ID", ""+id); h = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { System.out.println("Uncaught exception: " + ex); ex.printStackTrace(); Context context = getApplicationContext(); CharSequence text = "Network is not available, enable a wifi connection"; Toast toast = Toast.makeText(context, text, 2); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); android.content.Intent intent = new Intent(thisClass, MainActivity.class); startActivity(intent); finish(); } }; Thread c = new Thread() { @Override public void run() { // lock thread so it only accesses invoice after it's created lock.lock(); JSONObject result = null; String status = null; try { if(apiIsAGo) { while (notConfirmed) { Thread.sleep(800); try { // get the coinbase order result = coinBase.getHttp("https://coinbase.com/api/v1/orders/"+coinbaseOrderID); Log.d("URL", "https://coinbase.com/api/v1/orders/"+coinbaseOrderID); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //Log.d("Result", result.toString()); if(result!= null) { try { // get the status of the order JSONObject orderBlock = (JSONObject) result.get("order"); status = (String) orderBlock.get("status"); //Log.d("status", status); //Log.d("WHUT", result.toString()); } catch (Exception e) { Log.d("exception", "this means invoice not paid yet"+e); e.printStackTrace(); } if (status!=null && status.equals("completed")) { notConfirmed = false; // go to confirmed screen confirmed(); } } } } } catch (InterruptedException e) { // do nothing // pause is intendec } finally { lock.unlock(); } } }; c.setUncaughtExceptionHandler(h); c.start(); } /** * Check if the gocoin invoice is paid */ public void checkGoCoinInvoice() { // catch any uncaught exceptions thrown by getting invoice status // should be caught by gocoin class h = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { System.out.println("Uncaught exception: " + ex); Context context = getApplicationContext(); CharSequence text = "Network is not available, enable a wifi connection"; Toast toast = Toast.makeText(context, text, 2); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); android.content.Intent intent = new Intent(thisClass, MainActivity.class); startActivity(intent); finish(); } }; Thread c = new Thread() { @Override public void run() { // lock thread so it only accesses invoice after it's created lock.lock(); try { if(apiIsAGo) { while (notConfirmed) { Thread.sleep(800); // get status for gocoin invoice String status = goCoinInvoiceService.getInvoice(goCoinToken, goCoinInvoiceSetup.getId()).getStatus(); Log.d("status", status); if (status.equals("paid") || status.equals("confirmed") || status.equals("processing")) { notConfirmed = false; // go to confirmed screen confirmed(); } } } } catch (InterruptedException e) { // do nothing // pause is intended } finally { lock.unlock(); } } }; c.setUncaughtExceptionHandler(h); c.start(); } /** * Check if the wallet is paid */ public void checkWalletPaid() { // catch any uncaught exceptions thrown by getting invoice status // should be caught by gocoin class h = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { System.out.println("Uncaught exception: " + ex); Context context = getApplicationContext(); CharSequence text = "Network is not available, enable a wifi connection"; Toast toast = Toast.makeText(context, text, 2); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); android.content.Intent intent = new Intent(thisClass, MainActivity.class); startActivity(intent); finish(); } }; Thread c = new Thread() { @Override public void run() { // lock thread so it only accesses invoice after it's created lock.lock(); try { if(apiIsAGo) { while (notConfirmed) { Thread.sleep(800); // get status for wallet payment checkTxChain(); double unconfirmedBtc = ((double)(unconfirmed_balance)/10000000); View content = findViewById(android.R.id.content); content.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { return true; } }); double btcPaymentPrice = Double.parseDouble(btcPrice); //Log.d("BTC", ""+unconfirmedBtc+" "+initialWalletBalanceBtc); // initial wallet balance is the initial unconfirmed balance // see if payment received if ((unconfirmedBtc>=btcPaymentPrice && (unconfirmedBtc-initialWalletBalanceBtc)>=btcPaymentPrice)) { notConfirmed = false; // go to confirmed screen confirmed(); } } } } catch (InterruptedException e) { // do nothing // pause is intended } finally { lock.unlock(); } } }; c.setUncaughtExceptionHandler(h); c.start(); } @Override public void onBackPressed() { super.onBackPressed(); notConfirmed = false; } @Override public void onResume() { super.onResume(); apiIsAGo = true; notConfirmed = true; foreground = true; mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); if(whichPOS==1 && isNetworkAvailable()) { checkBitpayInvoice(); } else if(whichPOS==2 && isNetworkAvailable()) { if(coinbaseOrderID==null) { // do nothing } else { checkCoinbaseInvoice(); } } else if(whichPOS==3 && isNetworkAvailable()) { checkGoCoinInvoice(); } else if(whichPOS==4 && isNetworkAvailable()) { // check personal wallet invoice :D checkWalletPaid(); } else { Context context = getApplicationContext(); CharSequence text = "Network is not available, enable a wifi connection"; Toast toast = Toast.makeText(context, text, 2); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); // go to main screen android.content.Intent intent = new Intent(thisClass, MainActivity.class); startActivity(intent); finish(); } // resume nfc push if(!url.equals("") && url!=null && foreground && apiIsAGo && notConfirmed) { // mNfcAdapter.enableForegroundNdefPush(thisClass, thisClass.createNdefMessage()); mNfcAdapter = NfcAdapter.getDefaultAdapter(this); mNfcAdapter.setNdefPushMessageCallback(this, this); soundPlaying = true; String soundUrl = "b:"+btcAddress+"?"+btcPrice+"&"; sound = new NFSoundActivity(soundUrl, (AudioManager)this.getSystemService(Context.AUDIO_SERVICE)); Handler handlerBool = new Handler(); handlerBool.postDelayed(new Runnable() { @Override public void run() { soundPlaying = false; } }, 1400); // 5000ms delay // connection.resume(); } if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { processIntent(getIntent()); } // if ble supported then send data over ble // can remove nexus 5 line w/ android l official release try { if(isBLESupported(this)) { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { ble = new BluetoothUtility(thisClass); ble.setAdvertiseCallback(advertiseCallback); ble.setGattServerCallback(gattServerCallback); addServiceToGattServer(); ble.startAdvertise(); Log.d("BLE", "started advertising"); } }, 3500); } } catch (NullPointerException e) { Log.d("Error", e.toString()); e.printStackTrace(); // if fail due to android low level error try one more time try { if(isBLESupported(this)) { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { ble = new BluetoothUtility(thisClass); ble.setAdvertiseCallback(advertiseCallback); ble.setGattServerCallback(gattServerCallback); addServiceToGattServer(); ble.startAdvertise(); Log.d("BLE", "started advertising"); } }, 3500); } } catch (NullPointerException ex) { Log.d("Error", ex.toString()); ex.printStackTrace(); } } } /** * Add the initial base service to the gatt server (required to show up as advertisement) */ private void addServiceToGattServer() { serviceOneCharUuid = UUID.randomUUID().toString(); BluetoothGattService firstService = new BluetoothGattService( UUID.fromString(SERVICE_UUID_1), BluetoothGattService.SERVICE_TYPE_PRIMARY); // alert level char. BluetoothGattCharacteristic firstServiceChar = new BluetoothGattCharacteristic( UUID.fromString(serviceOneCharUuid), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE, BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE); firstService.addCharacteristic(firstServiceChar); ble.addService(firstService); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); } @Override public void onPause() { foreground = false; sound = null; soundPlaying = false; //if (connection!=null) { // connection.suspend(); //} mSensorManager.unregisterListener(this); if (mNfcAdapter != null) { //mNfcAdapter.disableForegroundNdefPush(thisClass); } super.onPause(); } @Override public void onStop() { notConfirmed = false; apiIsAGo = false; foreground = false; soundPlaying = false; sound = null; try { ble.cleanUp(); } catch (Exception e) { // wasn't created } super.onStop(); } @Override public void onDestroy() { try { ble.cleanUp(); } catch (Exception e) { // was already cleaned up in on stop } super.onDestroy(); } @Override public void onNewIntent(Intent intent) { setIntent(intent); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); setContentView(R.layout.activity_display_message); doQRCreation(btcAddress, btcPrice); } /** * Used to process response from NFC, though there shouldn't really be one * except maybe payment sent * @param intent */ @TargetApi(Build.VERSION_CODES.GINGERBREAD) void processIntent(Intent intent) { textView = (TextView) findViewById(R.id.nfcResponse); Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); NdefMessage msg = (NdefMessage) rawMsgs[0]; // textView.setText(new String(msg.getRecords()[0].getPayload())); } /** * Check if a internet connection is available * @return true if network is available */ private boolean isNetworkAvailable() { ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); return activeNetworkInfo != null && activeNetworkInfo.isConnected(); } public void refundOrder(View view) { // bitpay if(whichPOS==1) { Context context = getApplicationContext(); CharSequence text = "Bitcoin payment protocol refunds aren't available with Bitpay yet, please ask them about it!"; Toast toast = Toast.makeText(context, text, 2); //toast.setGravity(Gravity.CENTER, 0,5); toast.show(); } // coinbase else if (whichPOS==2) { // Creates an invoice using params passed from mainactivity // create the exception handler to catch uncaught exception from bitpay class // e.g. null apikey Thread.UncaughtExceptionHandler handle = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { System.out.println("Uncaught exception: " + ex); ex.printStackTrace(); Context context = getApplicationContext(); CharSequence text = "Network is not available, enable a wifi connection"; Toast toast = Toast.makeText(context, text, 2); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); android.content.Intent intent = new Intent(thisClass, MainActivity.class); startActivity(intent); } }; // new thread needed for network activities Thread t = new Thread() { @Override public void run() { // have to use lock b/c need to check for invoice in while loop later refundLock.lock(); try { String mispaymentsID = ""; // check for null api keys and redirect to settings if it's so!!! // else create bitpay invoice and get its url try { Log.d("W", "coinbase created"); Log.d("W", coinbaseApiKey); Log.d("W", coinbaseSecret); JSONObject refundCoinbase = new JSONObject(); refundCoinbase.put("instant_buy", "true"); refundCoinbase.put("refund_iso_code", "USD"); JSONObject json = new JSONObject(); json.put("order", refundCoinbase); Log.d("REFUND", json.toString()); try { JSONObject orderResult = coinBase.getHttp("https://coinbase.com/api/v1/orders/"+coinbaseOrderID); Log.d("RESULT", orderResult.toJSONString()); Log.d("Result", orderResult.toString()); JSONObject order = (JSONObject) orderResult.get("order"); org.json.simple.JSONArray mispayments = (org.json.simple.JSONArray) order.get("mispayments"); JSONObject misPayObj = (JSONObject) mispayments.get(0); mispaymentsID = (String) misPayObj.get("id"); Log.d("WOW", mispayments.toString() + " " + mispaymentsID); if(mispaymentsID!=null && !mispaymentsID.equals("")) { refundCoinbase.put("mispayment_id", mispaymentsID); } } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (NullPointerException e) { // order not paid yet Log.d("exceptionw", e.toString()); e.printStackTrace(); } try { // use coinbaseorderid after payment done otherwise mispamentid result = coinBase.postHttp("https://coinbase.com/api/v1/orders/"+coinbaseOrderID+"/refund", json.toString()); JSONObject orderResult = coinBase.getHttp("https://coinbase.com/api/v1/orders/"+coinbaseOrderID); Log.d("URL", "https://coinbase.com/api/v1/orders/"+coinbaseOrderID); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // JSONObject s = (JSONObject) result.get("order"); // s.get("status"); // Log.d("WHAT", (String) s.get("status")); Log.d("REFUNDRESULT", result.toString()); if(result.toString().contains("No refund address is present")) { Context context = getApplicationContext(); CharSequence text = "Customer wallet doesn't support bip70 (refund protocol) \n manually send a refund to the customer's address \n from your coinbase account"; Toast toast = Toast.makeText(context, text, 2); //toast.setGravity(Gravity.CENTER, 0,5); toast.show(); } else if(result.toString().contains("fee to be accepted")) { Context context = getApplicationContext(); CharSequence text = "Amount is too small for Coinbase auto refund \n manually send a refund to the customer's address \n from your coinbase account"; Toast toast = Toast.makeText(context, text, 2); //toast.setGravity(Gravity.CENTER, 0,5); toast.show(); } else if(result.toString().contains("true")) { Context context = getApplicationContext(); CharSequence text = "Payment Refunded"; Toast toast = Toast.makeText(context, text, 2); //toast.setGravity(Gravity.CENTER, 0,5); toast.show(); } // JSONObject order = (JSONObject) result.get("order"); // JSONObject button = (JSONObject) order.get("button"); } // if api key is wrong we get a null pointer // go to settings screen in this case catch (NullPointerException e) { apiIsAGo = false; android.content.Intent intentSettings = new Intent(thisClass, SettingsActivity.class); intentSettings.putExtra("apifail?", true); startActivity(intentSettings); finish(); Log.d("AA", "" + apiIsAGo+e); e.printStackTrace(); } } finally { // other threads can now access this info refundLock.unlock(); } } }; t.setUncaughtExceptionHandler(handle); t.start(); // checkCoinbaseInvoice(); } // gocoin else if (whichPOS==3) { Context context = getApplicationContext(); CharSequence text = "Refunds aren't available with GoCoin yet, please ask them about it!"; Toast toast = Toast.makeText(context, text, 2); //toast.setGravity(Gravity.CENTER, 0,5); toast.show(); } // personal wallet else { Context context = getApplicationContext(); CharSequence text = "Please make a refund from your personal wallet"; Toast toast = Toast.makeText(context, text, 2); //toast.setGravity(Gravity.CENTER, 0,5); toast.show(); } } /** * Called on a successful (or failed) bluetooth LE advertising */ private AdvertiseCallback advertiseCallback = new AdvertiseCallback() { @Override public void onSuccess(AdvertiseSettings advertiseSettings) { String successMsg = "Advertisement command attempt successful"; Log.d("BLE", successMsg); } @Override public void onFailure(int i) { String failMsg = "Advertisement command attempt failed: " + i; Log.e("BLE", failMsg); } }; /** * Once advertising is started, a gatt server is created with this callback */ public BluetoothGattServerCallback gattServerCallback = new BluetoothGattServerCallback() { @Override public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { super.onConnectionStateChange(device, status, newState); Log.d("BLE", "onConnectionStateChange status=" + status + "->" + newState); } @Override public void onServiceAdded(int status, BluetoothGattService service) { super.onServiceAdded(status, service); } // if char read request then set 3 char values, then send the value as a response to the bluetooth device // that requested it @Override public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) { super.onCharacteristicReadRequest(device, requestId, offset, characteristic); Log.d("BLE", "onCharacteristicReadRequest requestId=" + requestId + " offset=" + offset); try { String bitcoinURI = url.substring(url.indexOf("b"), url.indexOf("&")+1); Log.d("BLE", bitcoinURI); // first 19 chars String partOne = bitcoinURI.substring(bitcoinURI.indexOf("b"), bitcoinURI.indexOf("b")+19); // next 19 chars String partTwo = bitcoinURI.substring(bitcoinURI.indexOf("b")+19, bitcoinURI.indexOf("b")+38); // last set of chars (20) String partThree = bitcoinURI.substring(bitcoinURI.indexOf("b")+38, bitcoinURI.indexOf("&")+1); // Log.d("WOAH", partOne+partTwo+partThree); if (characteristic.getUuid().equals(UUID.fromString("230f04b4-42ff-4ce9-94cb-ed0dc8231957"))) { characteristic.setValue(partOne); ble.getGattServer().sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue()); } if (characteristic.getUuid().equals(UUID.fromString("230f04b4-42ff-4ce9-94cb-ed0dc8231958"))) { characteristic.setValue(partTwo); ble.getGattServer().sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue()); } if (characteristic.getUuid().equals(UUID.fromString("230f04b4-42ff-4ce9-94cb-ed0dc8231959"))) { characteristic.setValue(partThree); ble.getGattServer().sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue()); } } catch (Exception e) { // uri not created yet } } /** * Writes a value to a ble characteristic * @param device * @param requestId * @param characteristic * @param preparedWrite * @param responseNeeded * @param offset * @param value */ @Override public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value); Log.d("BLE", "onCharacteristicWriteRequest requestId=" + requestId + " preparedWrite=" + Boolean.toString(preparedWrite) + " responseNeeded=" + Boolean.toString(responseNeeded) + " offset=" + offset); } }; }